﻿using System.Collections.Generic;
using Demo.Framework.GraphicsMaterials;
using Demo.Framework.PostProcessing;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Demo.Framework.Business;

namespace Demo.Framework
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class ModelManager : DrawableGameComponent
    {
        private Renderer _renderer;
        private SkySphere _skyBox;
        private readonly Dictionary<string, BasicModel> _modelDictionary = new Dictionary<string, BasicModel>();
        RenderCapture _depthCapture;
        private RenderCapture _renderCapture;
        private GaussianBlur _postprocessor;
        private Effect _depthEffect;
        private RenderCapture _blurCapture;
        private DepthOfField _dof;

        public ModelManager(Game game)
            : base(game)
        {
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
            _renderer = new Renderer(GraphicsDevice);
            _skyBox = new SkySphere(Game.Content, GraphicsDevice);
            _depthCapture = new RenderCapture(GraphicsDevice, SurfaceFormat.HalfSingle);
            _renderCapture = new RenderCapture(GraphicsDevice);
            _postprocessor = new GaussianBlur(GraphicsDevice, Game.Content, 2);

            _depthEffect = Game.Content.Load<Effect>("Effects/DepthEffect");
            _blurCapture = new RenderCapture(GraphicsDevice, SurfaceFormat.Color);
            _dof = new DepthOfField(GraphicsDevice, Game.Content);
        }

        public void AddModel(string name,string path)
        {
            _modelDictionary.Add(name, new BasicModel(Game.Content.Load<Model>(path), GraphicsDevice));
            //_models.Add(new BasicModel(Game.Content.Load<Model>(name), GraphicsDevice));
        }

        public void AddModel(string name, string path, Vector3 position, bool player)
        {
            var m = new BasicModel(Game.Content.Load<Model>(path), GraphicsDevice);
            m.Translate(position);
            m.player = player;
            _modelDictionary.Add(name, m);
            //_models.Add(m);
        }

        //public void AddModel(BasicModel model)
        //{
        //    _models.Add(model);
        //}

        public void LoadShaderForAllModels()
        {
            var simpleEffect = Game.Content.Load<Effect>("Effects\\MultiLightningEffect");

            var mat = new LightingMaterial();

            var mat2 = new PointLightMaterial();
            mat2.LightPosition = new Vector3(0, 1500, 1500);
            mat2.LightAttenuation = 3000;

            var mat3 = new SpotLightMaterial();
            mat3.LightDirection = new Vector3(0, -1, -1);
            mat3.LightPosition = new Vector3(0, 3000, 2700);
            mat3.LightFalloff = 200;

            var mat4 = new MultiLightingMaterial();
            BasicEffect effect = new BasicEffect(GraphicsDevice);
            effect.EnableDefaultLighting();
            mat4.LightDirection[0] = -effect.DirectionalLight0.Direction;
            mat4.LightDirection[1] = -effect.DirectionalLight1.Direction;
            mat4.LightDirection[2] = -effect.DirectionalLight2.Direction;
            mat4.LightColor = new Vector3[]
                {
                    new Vector3(0.5f, 0.5f, 0.5f),
                    new Vector3(0.5f, 0.5f, 0.5f),
                    new Vector3(0.5f, 0.5f, 0.5f)
                };


            Effect normalMapEffect = Game.Content.Load<Effect>("Effects\\NormalMapEffect");
            NormalMapMaterial normalMat = new NormalMapMaterial(
               Game.Content.Load<Texture2D>("Textures\\brick_normal_map"));
            normalMat.LightDirection = new Vector3(.5f, .5f, 1);
            normalMat.LightColor = Vector3.One;

            foreach (var model in _modelDictionary)
            {
                model.Value.SetModelEffect(simpleEffect, true);
                model.Value.Material = mat4;
            }

            /*_modelDictionary["Tunel1"].SetModelEffect(normalMapEffect, true);
            _modelDictionary["Tunel1"].Material = normalMat;

            _modelDictionary["Tunel2"].SetModelEffect(normalMapEffect, true);
            _modelDictionary["Tunel2"].Material = normalMat;*/

            /*foreach (var basicModel in _models)
            {
                basicModel.SetModelEffect(simpleEffect, true);
                basicModel.Material = mat4;
            }*/

            /*_models[11].SetModelEffect(normalMapEffect, true);
            _models[11].Material = normalMat;

            _models[12].SetModelEffect(normalMapEffect, true);
            _models[12].Material = normalMat;*/
        }

        public BasicModel GetModel(string name)
        {
            return _modelDictionary[name];
        }

        protected override void LoadContent()
        {

            base.LoadContent();
        }

        protected override void UnloadContent()
        {
            _modelDictionary.Clear();
            base.UnloadContent();
            _skyBox.Dispose();
        }

        public override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            if (GraphicSettings.EnabledBlur)
                _renderer.DrawWithBlur(DrawScene, gameTime);
            else
                DrawScene(gameTime);

            base.Draw(gameTime);
        }

        private void DrawScene(GameTime gameTime)
        {
            GraphicsDevice.BlendState = BlendState.Opaque;
            var d = new DepthStencilState {DepthBufferEnable = true};
            GraphicsDevice.DepthStencilState = d;

            // Start rendering to depth map
            _depthCapture.Begin();

            // Clear to white (max depth)
            GraphicsDevice.Clear(Color.White);

            _skyBox.Draw();

            foreach (BasicModel model in _modelDictionary.Values)
            {
                model.CacheEffects(); // Cache effect
                model.SetModelEffect(_depthEffect, false); // Set depth effect
                model.Draw();
                model.RestoreEffects(); // Restore effects
            }

            // Finish rendering to depth map
            _depthCapture.End();

            // Begin rendering the main render
            _renderCapture.Begin();

            GraphicsDevice.Clear(Color.CornflowerBlue);

            _skyBox.Draw();

            // Draw all models
            foreach (BasicModel model in _modelDictionary.Values)
                model.Draw();

            // Finish the main render
            _renderCapture.End();

            // Prepare to blur results of main render
            _postprocessor.Input = _renderCapture.GetTexture();
            // Output blur to our RenderCapture
            _postprocessor.ResultCapture = _blurCapture;
            // Perform blur
            _postprocessor.Draw();

            // Set the three images to the DOF class
            _dof.DepthMap = _depthCapture.GetTexture();
            _dof.Unblurred = _renderCapture.GetTexture();
            _dof.Input = _postprocessor.ResultCapture.GetTexture();

            // Combine the images into the final result
            _dof.Draw();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // Loop through all models and call Update
            foreach (var model in _modelDictionary.Values)
            {
                model.Update();
            }
            base.Update(gameTime);
        }
    }
}
